package cn.wx.common.persistence.base;

import com.baomidou.mybatisplus.enums.SqlMethod;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.SqlHelper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.toolkit.MapUtils;
import com.baomidou.mybatisplus.toolkit.ReflectionKit;

import cn.wx.common.persistence.exception.MyException;
import cn.wx.common.persistence.web.ResponseCode;
import cn.wx.common.utils.ObjectTranslator;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.List;
import java.util.Map;


/**
 *   @description : service 基类
 *   ---------------------------------
 *   @author zj
 *   @since 2017-10-27
 */
@SuppressWarnings({ "unchecked" })
public class BaseService<T extends BaseEntity, M extends SimpleMapper<T>> {

	@Autowired
	protected M simpleMapper;

	/**
	 * 根据id获取单个对象
	 * 
	 * @param id
	 * @return
	 */
	@Transactional(readOnly = true)
	public T queryUniqueByXML(BaseQ Q) throws MyException{
		List<T> list = queryList(Q);
		if(list !=null && list.size()>0)return list.get(0);
		return null;
	}
	
	@Transactional(readOnly = true)
	public T queryUnique(BaseQ Q,Class<T> clazz) throws MyException{
		T entity;
		try {
			entity = clazz.newInstance();
			BeanUtils.copyProperties(Q,entity);
			List<T> list = selectList(entity);
			if(list !=null && list.size()>0)return list.get(0);
		} catch (InstantiationException | IllegalAccessException e) {
			e.printStackTrace();
		}
		
		return null;
	}
	
	/**
	 * 不分页查询list
	 * 
	 * @param qo
	 * @return
	 */
	@Transactional(readOnly = true)
	public List<T> queryList(BaseQ Q) throws MyException{
		return simpleMapper.queryList(entityToMap(Q));
	}

	/**
	 * 参照xml查询分页对象
	 * 
	 * @param qo
	 * @return
	 */
	@Transactional(readOnly = true)
	public Page<T> queryPage(BaseQ Q) throws MyException {
		Page<T> page = initPage(Q);
		Map<String, Object> qm = entityToMap(Q);
		page.setRecords(simpleMapper.queryList(page,qm));
		page.setCondition(qm);
		return page;
	}
	
	/**
	 * 将实体对象转化map对象
	 * @param entity
	 * @return
	 */
	protected Map<String, Object> entityToMap(BaseQ Q){
		return ObjectTranslator.objectToMap(Q);
	}
	
	/**
	 * 获取分页对象
	 * @throws DbException 
	 */
	protected <K>Page<K> initPage(BaseQ Q) throws MyException{
		/**
		 * 验证分页参数
		 */
		if(!Q.checkCanPage()){
			throw new MyException(ResponseCode.PARAMETER_REQUIRED);
		}
		
		return new Page<K>(Q.getPageNo(), Q.getPageSize());
	}
	
	
	/**
	 * 新增
	 * 
	 * @param entity
	 * @return
	 * @throws DbException
	 */
	@Transactional
	public boolean insert(T entity) throws MyException {
		// 业务主键UUID
		return retBool(simpleMapper.insert(entity));
	}

	/**
	 * 批量插入
	 * 
	 * @param entityList
	 * @return
	 * @throws DbException
	 */
	@Transactional
	public boolean insertBatch(List<T> entityList) throws MyException {
		return insertBatch(entityList, 30);
	}

	/**
	 * 批量插入
	 * 
	 * @throws DbException
	 */
	@Transactional
	public boolean insertBatch(List<T> entityList, int batchSize) throws MyException {
		if (CollectionUtils.isEmpty(entityList)) {
			throw new MyException(ResponseCode.PARAMETER_REQUIRED);
		}
		try (SqlSession batchSqlSession = sqlSessionBatch()) {
			int size = entityList.size();
			String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
			for (int i = 0; i < size; i++) {
				batchSqlSession.insert(sqlStatement, entityList.get(i));
				if (i >= 1 && i % batchSize == 0) {
					batchSqlSession.flushStatements();
				}
			}
			batchSqlSession.flushStatements();
		} catch (Throwable e) {
			e.printStackTrace();
			throw new MyException(ResponseCode.INSERT_FAIL);
		}
		return true;
	}

	/**
	 * 批量更新
	 * 
	 * @throws DbException
	 */
	@Transactional
	public boolean updateBatch(List<T> entityList, int batchSize) throws MyException {
		if (CollectionUtils.isEmpty(entityList)) {
			throw new MyException(ResponseCode.PARAMETER_REQUIRED);
		}
		try (SqlSession batchSqlSession = sqlSessionBatch()) {
			int size = entityList.size();
			String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
			for (int i = 0; i < size; i++) {
				batchSqlSession.update(sqlStatement, entityList.get(i));
				if (i >= 1 && i % batchSize == 0) {
					batchSqlSession.flushStatements();
				}
			}
			batchSqlSession.flushStatements();
		} catch (Throwable e) {
			e.printStackTrace();
			throw new MyException(ResponseCode.INSERT_FAIL);
		}
		return true;
	}
	
	/**
	 * 批量插入
	 * 
	 * @throws DbException
	 */
	@Transactional
	public boolean insertBatchByXml(List<T> entityList, int batchSize) throws MyException {
		
		if (CollectionUtils.isEmpty(entityList)) {
			throw new MyException(ResponseCode.PARAMETER_REQUIRED);
		}
		
		try (SqlSession batchSqlSession = sqlSessionBatch()) {
			
			int size = entityList.size();
			
			String sqlStatement = sqlStatementByXml();
			
			for (int i = 0; i < size; i++) {
				batchSqlSession.insert(sqlStatement, entityList.get(i));
				if (i >= 1 && i % batchSize == 0) {
					batchSqlSession.flushStatements();
				}
			}
			batchSqlSession.flushStatements();
		} catch (Throwable e) {
			e.printStackTrace();
			throw new MyException(ResponseCode.INSERT_FAIL);
		}
		return true;
	}
	
	
	/**
	 * 修改
	 * 
	 * @param entity
	 * @return
	 * @throws MyException
	 */
	@Transactional
	public synchronized boolean update(T entity) throws MyException {
		return retBool(simpleMapper.updateById(entity));
	}
	
	@Transactional
	public synchronized boolean update(T entity, Wrapper<T> wrapper) {
		return retBool(simpleMapper.update(entity, wrapper));
	}
	
	/**
	 * 删除根据id
	 * 
	 * @param id
	 * @return
	 */
	@Transactional
	public synchronized boolean delete(Serializable id) {
		return retBool(simpleMapper.deleteById(id));
	}

	/**
	 * 删除根据实体
	 * 
	 * @param entity
	 * @return
	 */
	@Transactional
	public boolean delete(T entity) {
		Wrapper<T> wrapper = new EntityWrapper<T>(entity);
		return retBool(simpleMapper.delete(wrapper));
	}

	/**
	 * 批量删除根据物理pids
	 * 
	 * @param entity
	 * @return
	 */
	@Transactional
	public boolean deleteBatchPids(List<? extends Serializable> idList) {
		return retBool(simpleMapper.deleteBatchIds(idList));
	}

	/**
	 * 根据map删除
	 * 
	 * @param map
	 * @return
	 * @throws DbException
	 */
	@Transactional
	public boolean deleteByMap(Map<String, Object> map) throws MyException {
		if (MapUtils.isEmpty(map)) {
			throw new MyException(ResponseCode.PARAMETER_REQUIRED);
		}
		return retBool(simpleMapper.deleteByMap(map));
	}

	/**
	 * 根据id获取单个对象
	 * 
	 * @param id
	 * @return
	 */
	@Transactional(readOnly = true)
	public T getOne(Serializable id) {
		return simpleMapper.selectById(id);
	}

	/**
	 * 根据map查询列表对象
	 * 
	 * @param columnMap
	 * @return
	 */
	@Transactional(readOnly = true)
	public List<T> getBatchByMap(Map<String, Object> map) {
		return simpleMapper.selectByMap(map);
	}

	/**
	 * 根据查询实体查询列表对象
	 * 
	 * @param columnMap
	 * @return
	 */
	@Transactional(readOnly = true)
	public List<T> selectList(T entity) {
		Wrapper<T> wrapper = new EntityWrapper<T>(entity);
		return simpleMapper.selectList(wrapper);
	}

	/**
	 * 根据id列表获取列表对象
	 * 
	 * @param id
	 * @return
	 */
	@Transactional(readOnly = true)
	public List<T> getBatchByIds(List<? extends Serializable> idList) {
		return simpleMapper.selectBatchIds(idList);
	}

	/**
	 * 判断数据库操作是否成功 注意！！ 该方法为 Integer 判断，不可传入 int 基本类型
	 * 
	 * @param result
	 *            数据库操作返回影响条数
	 * @return boolean
	 */
	protected static boolean retBool(Integer result) {
		return SqlHelper.retBool(result);
	}

	protected Class<T> currentModelClass() {
		return ReflectionKit.getSuperClassGenricType(getClass(), 0);
	}

	/**
	 * 批量操作 SqlSession
	 */
	protected SqlSession sqlSessionBatch() {
		return SqlHelper.sqlSessionBatch(currentModelClass());
	}

	/**
	 * 获取SqlStatement
	 */
	protected String sqlStatement(SqlMethod sqlMethod) {
		return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod());
	}
	
	/**
	 * 获取SqlStatement
	 */
	protected String sqlStatementByXml() {
		return SqlHelper.table(currentModelClass()).getSqlStatement("insertBatch");
	}

}
